package com.yilin.platform.base_core.commons.utils;

import org.springframework.lang.Nullable;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Logger;

public class FieldUtil {
    private static Logger logger = Logger.getLogger(FieldUtil.class.getName());

    /*
     * 根据属性名获取属性类型
     * */
    public static Class getFiledType(String fieldName, Class clazz) {
        ArrayList<Field> list = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环 遍历父类
            list.addAll(Arrays.asList(clazz.getDeclaredFields()));
        }
        for (Field field : list) {
            if (Objects.equals(fieldName, field.getName())) {
                return field.getType();
            }
        }
        return null;
    }

    public static Field getField(Class c, String fieldName) {
        Field javaField = null;
        try {
            if (FieldUtil.hasField(c, fieldName)) {
                javaField = c.getDeclaredField(fieldName);
            } else {
                for (; c != Object.class && javaField == null; c = c.getSuperclass()) {//向上循环 遍历父类
                    if (FieldUtil.hasField(c, fieldName)) {
                        javaField = c.getDeclaredField(fieldName);
                    }
                }
            }
        } catch (NoSuchFieldException e) {
            return null;
        }
        return javaField;
    }

    public static List<Field> getFields(Class c) {
        List<Field> javaFields = new ArrayList<>(Arrays.asList(c.getDeclaredFields()));
        for (; c != Object.class; c = c.getSuperclass()) {//向上循环 遍历父类
           ArrayUtil.notRepeatAddAll(javaFields,Arrays.asList(c.getDeclaredFields()));
        }
        return javaFields;
    }


    private static boolean hasField(Class c, String fieldName) {
        Field[] fields = c.getDeclaredFields();
        for (Field f : fields) {
            if (fieldName.equals(f.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取实例化名称
     * 查询工具类
     */
    public static String getObjName(Class cls) {
        String objName = cls.getName();
        if (objName.contains(".")) {
            String[] splits = objName.split("\\.");
            objName = splits[splits.length - 1];
            objName = StringUtil.toLowerCaseFirstOne(objName);
        }
        return objName;
    }

    /**
     * 根据表注解获取对象
     */
    private static Set<Class<?>> annotationClassSet;//扫描数据只需要第一次
    public static List<Class<?>> getEntityAnnotation(Class<? extends Annotation > cls) {
        if (annotationClassSet!=null)  return new ArrayList<>(annotationClassSet);
        String[] packages = new String[]{"com.yilin.xbr.entity.*"};
        ScanPackageFactory scanPackageFactory = new ScanPackageFactory(packages, cls);
        try {
            annotationClassSet = scanPackageFactory.getClassSet();
            return new ArrayList<>(annotationClassSet);
        } catch (IOException | ClassNotFoundException e) {
            return new ArrayList<>();
        }
    }

    /**
     * 通过注解反射 获得对象值
     * 提供表名和数据库字段
     */
    public static <E> E convertEntity(Class<E> cls, Map<String, Object> fls) {
        try {
            Constructor<E> con = cls.getDeclaredConstructor();
            E entity = con.newInstance();
            for (String key : fls.keySet()) {
                Field objField = getField(cls, key);
                if (objField == null){
                    logger.warning( "警告：没有‘"+key+"’的实体属性，已跳过！");
                    continue;
                }
                entity = objSet(entity,objField,fls.get(key));
            }
            return entity;
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            logger.warning( "转化失败");
        }
        return null;
    }

    /**
     * 获取注解字段
     */
    public static List<Field> getFieldsByAnnotation(Class cls, Class<? extends Annotation> a) {
        List<Field> fields = getFields(cls);
        List<Field> list= new ArrayList<>();
        for (Field field : fields) {
            Annotation column = field.getAnnotation(a);
            if (column == null) continue;
            list.add(field);
        }
        return list;
    }


    /**
     * 操作对象SET
     */
    private static<T> T objSet(T o, Field field,Object value) {
        try {
            field.setAccessible(true); // 打破封装
            if (field.getType().equals(Date.class)){
                field.set(o, value==null?null:new Date((Long) value));
            }else if (field.getType().isEnum()){
                if (value==null) field.set(o,null);
                else {
                    Object[] objects = field.getType().getEnumConstants();
                    field.set(o,objects[(int)value]);
                }
            }else{
                field.set(o, value);
            }
            return o;
        } catch (Exception e) {
            logger.warning(o.getClass().getName() + ": [ERROR] 属性'" + field.getName() + "'操作失败.");
        }
        return o;
    }

    /**
     * 返回对象GET
     * */
    public static Object objGet(Object o,  Field field) {
        if (o==null) return null;
        try {
            field.setAccessible(true); // 打破封装
            return field.get(o);
        } catch (Exception e) {
            logger.warning(o.getClass().getName() + ": [ERROR] 属性'" + field.getName() + "'操作失败.");
        }
        return null;
    }
    /**
     * 赋值指定字段给另外一个实体
     * @param   source 有值的实体
     * @param   target 需要赋值的实体
     * @param   fieldNames 指定字段，如果没有 默认赋值平台信息
     * */
    public static <T, U> void copyFields(T source, U target,@Nullable String[] fieldNames) {
        if(fieldNames==null||fieldNames.length<1) fieldNames = new String[]{"zonePlatId", "zonePlatName", "platId", "platName"};
        try {
            for (String fieldName : fieldNames) {
                String capitalizedFieldName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
                Method sourceGetter = source.getClass().getMethod("get" + capitalizedFieldName);
                Method targetSetter = target.getClass().getMethod("set" + capitalizedFieldName, sourceGetter.getReturnType());
                Object fieldValue = sourceGetter.invoke(source);
//                if (fieldValue != null) {
                    targetSetter.invoke(target, fieldValue);
//                }
            }
        } catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }


}
